/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#ifndef ARM
# define _XOPEN_SOURCE
# define _GNU_SOURCE
# include <stdlib.h>
# include <limits.h>
#endif

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

#include "uart.h"
#include "hw_module.h"
#include "log.h"



static int int_to_baud (int baud)
{
  switch (baud) {
    case 0: return B0;
    case 50: return B50;
    case 75: return B75;
    case 110: return B110;
    case 134: return B134;
    case 150: return B150;
    case 200: return B200;
    case 300: return B300;
    case 600: return B600;
    case 1200: return B1200;
    case 1800: return B1800;
    case 2400: return B2400;
    case 4800: return B4800;
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    default: break;
  }

  return B9600;
}

int uart_create (struct uart *uart, struct uart_hw_module *mod, int baud)
{
  struct termios ios;

  if (!uart || !mod)
    return -1;

  if (hw_module_enable (mod->hw) == -1) {
    log (LOG_ERR, "unable to enable UART hw module");
    return -1;
  }

  uart->fd = open (mod->dev_path, O_RDWR | O_NOCTTY);
  if (uart->fd == -1) {
    log (LOG_ERR, "unable to open UART device");
    return -1;
  }

  if (tcgetattr (uart->fd, &uart->old_ios) == -1) {
    log (LOG_ERR, "unable to get UART attributes");
    goto err_out;
  }

  memset (&ios, 0, sizeof (ios));

  ios.c_cc[VMIN] = 1;

  cfsetispeed (&ios, int_to_baud (baud));
  cfsetospeed (&ios, int_to_baud (baud));

  if (tcsetattr (uart->fd, TCSANOW, &ios) == -1) {
    log (LOG_ERR, "unable to set UART attributes");
    goto err_out;
  }

#ifndef ARM
  if (grantpt (uart->fd) == -1) {
    log (LOG_ERR, "unable to grant PT");
    goto err_out;
  }

  if (unlockpt (uart->fd) == -1) {
    log (LOG_ERR, "unable to unlock PT");
    goto err_out;
  }
#endif

  return 0;

err_out:
  close (uart->fd);

  return -1;
}

void uart_destroy (struct uart *uart)
{
  if (!uart)
    return;

  tcsetattr (uart->fd, TCSANOW, &uart->old_ios);

  close (uart->fd);
}

ssize_t uart_read (struct uart *uart, void *buff, size_t count)
{
  if (!uart)
    return -1;

  return read (uart->fd, buff, count);
}

ssize_t uart_write (struct uart *uart, const void *buff, size_t count)
{
  if (!uart)
    return -1;

  return write (uart->fd, buff, count);
}

int uart_get_fd (struct uart *uart)
{
  if (!uart)
    return -1;

  return uart->fd;
}

#ifndef ARM
int uart_open_slave_pt (struct uart *uart)
{
  char pt_name[PATH_MAX];
  int fd;

  if (!uart)
    return -1;

  if (ptsname_r (uart->fd, pt_name, PATH_MAX) != 0)
    return -1;

  fd = open (pt_name, O_RDWR | O_NOCTTY);

  return fd;
}
#endif
